#include <stdio.h>

#include "../hle.h"

#include "__sceUtility.h"

/*************
 * SAVEDATAS *
 *************/

enum SceUtilitySavedataType
{
    SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD        = 0,
    SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE        = 1,
    SCE_UTILITY_SAVEDATA_TYPE_LOAD            = 2,
    SCE_UTILITY_SAVEDATA_TYPE_SAVE            = 3,
    SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD        = 4,
    SCE_UTILITY_SAVEDATA_TYPE_LISTSAVE        = 5,
    SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE      = 6,
    SCE_UTILITY_SAVEDATA_TYPE_DELETE          = 7,
    SCE_UTILITY_SAVEDATA_TYPE_SIZES           = 8
};

#define SCE_UTILITY_SAVEDATA_ERROR_TYPE                 (0x80110300)

#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_MS           (0x80110301)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_EJECT_MS        (0x80110302)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_ACCESS_ERROR    (0x80110305)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN     (0x80110306)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA         (0x80110307)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM           (0x80110308)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_INTERNAL        (0x8011030b)

#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_MS           (0x80110381)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_EJECT_MS        (0x80110382)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE      (0x80110383)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_PROTECTED    (0x80110384)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_ACCESS_ERROR    (0x80110385)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM           (0x80110388)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_UMD          (0x80110389)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_WRONG_UMD       (0x8011038a)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_INTERNAL        (0x8011038b)

#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_MS         (0x80110341)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_EJECT_MS      (0x80110342)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_MS_PROTECTED  (0x80110344)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_ACCESS_ERROR  (0x80110345)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA       (0x80110347)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_PARAM         (0x80110348)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_INTERNAL      (0x8011034b)

#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_MS          (0x801103c1)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_EJECT_MS       (0x801103c2)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_ACCESS_ERROR   (0x801103c5)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA        (0x801103c7)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_PARAM          (0x801103c8)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_UMD         (0x801103c9)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_WRONG_UMD      (0x801103ca)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_INTERNAL       (0x801103cb)

/* Contains what the VSH's load screen shows */
typedef struct
{
    s8 title[0x80];
    s8 savedataTitle[0x80];
    s8 detail[0x400];
    u8 parentalLevel;
    u8 unknown[3];
} PspUtilitySavedataSFOParam;

typedef struct
{
    void *buf;
    SceSize bufSize;
    SceSize size;
    s32 unknown;
} PspUtilitySavedataFileData;

typedef struct
{
    SceSize size;

    u32 language;

    u32 buttonSwap;

    u32 threads[4];
    u32 result;
    u32 unknown[4];

    s32 mode; /* 0 to load, 1 to save */
    s32 bind;

    s32 overwriteMode;

    char gameName[16];
    char saveName[24];
    char fileName[16];

    u32 dataBuf; /* pointer to a buffer that will contain data file unencrypted data */
    SceSize dataBufSize; /* size of allocated space to dataBuf */
    SceSize dataSize;

    PspUtilitySavedataSFOParam sfoParam;

    PspUtilitySavedataFileData icon0FileData;
    PspUtilitySavedataFileData icon1FileData;
    PspUtilitySavedataFileData pic1FileData;
    PspUtilitySavedataFileData snd0FileData;

    u32 unknown2;
} SceUtilitySavedataParam;

SceUtilitySavedataParam curSavedataParam;
u32 curSavedataAddr;

void sceUtilitySavedataInitStart()
{
    mem_readStruct(PARAM(0), &curSavedataParam, sizeof(SceUtilitySavedataParam));
    curSavedataAddr = PARAM(0);
    _log(INF, HLE, "sceUtilitySavedataInitStart(%08x)", PARAM(0));
    __sceUtilityWinInitStart();
    RETURN(0);
}

void sceUtilitySavedataShutdownStart()
{
    _log(INF, HLE, "sceUtilitySavedataShutdownStart()");
    __sceUtilityWinShutdownStart();
    RETURN(0);
}

void sceUtilitySavedataUpdate()
{
    _log(INF, HLE, "sceUtilitySavedataUpdate(%d)", PARAM(0));
    if (__sceUtilityWinDoUpdate())
    {
        /* load */
        if (curSavedataParam.mode == 0) {
            _log(ERR, HLE, "Loading. Title: %s Save: %s File: %s", curSavedataParam.gameName, curSavedataParam.saveName, curSavedataParam.fileName);
            curSavedataParam.result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
        }
        /* save */
        else {
            _log(ERR, HLE, "Saving. Title: %s Save: %s File: %s", curSavedataParam.gameName, curSavedataParam.saveName, curSavedataParam.fileName);
            curSavedataParam.result = 0;
        }
        mem_writeStruct(curSavedataAddr, &curSavedataParam, sizeof(curSavedataParam));

        __sceUtilityWinFinish();
    }

    RETURN(0);
}

void sceUtilitySavedataGetStatus()
{
    u8 status = __sceUtilityWinGetStatus();
    _log(INF, HLE, "%d = sceUtilitySavedataGetStatus()", status);
    RETURN(status);
}

/***********
 * DIALOGS *
 ***********/

typedef struct
{
    u32 size;           /* Size of the structure */
    u32 language;       /* Language */
    u32 buttonSwap;     /* Set to 1 for X/O button swap */
    u32 graphicsThread; /* Graphics thread priority */
    u32 accessThread;   /* Access/fileio thread priority (SceJobThread) */
    u32 fontThread;     /* Font thread priority (ScePafThread) */
    u32 soundThread;    /* Sound thread priority */
    u32 result;         /* Result */
    u32 reserved[4];    /* Set to 0 */
} SceUtilityDialogCommon;

typedef enum
{
    MSGDIALOG_MODE_ERROR = 0, /* Error message */
    MSGDIALOG_MODE_TEXT /* String message */
} SceUtilityMsgDialogMode;

typedef enum
{
    MSGDIALOG_OPTION_ERROR         = 0,          /* Error message (why two flags?) */
    MSGDIALOG_OPTION_TEXT          = 0x00000001, /* Text message (why two flags?) */
    MSGDIALOG_OPTION_YESNO_BUTTONS = 0x00000010, /* Yes/No buttons instead of 'Cancel' */
    MSGDIALOG_OPTION_OK_BUTTON     = 0x00000020, /* Only Ok and Cancel buttons */
    MSGDIALOG_OPTION_DEFAULT_NO    = 0x00000100  /* Default position 'No', if not set will default to 'Yes' */
} SceUtilityMsgDialogOption;

typedef enum
{   
    MSGDIALOG_RESULT_UNKNOWN1 = 0,
    MSGDIALOG_RESULT_YES,
    MSGDIALOG_RESULT_NO,
    MSGDIALOG_RESULT_BACK
} SceUtilityMsgDialogPressed;

typedef struct
{   
    SceUtilityDialogCommon base;
    u32 unknown;
    SceUtilityMsgDialogMode mode;
    u32 errorValue;
    char message[512]; /* The message to display */
    u32 options; /* OR'ed sceUtilityMsgDialogOption */
    SceUtilityMsgDialogPressed buttonPressed;
} SceUtilityMsgDialogParams;

SceUtilityMsgDialogParams curMsgDialogParams;
u32 curMsgDialogAddr;

void sceUtilityMsgDialogInitStart()
{
    curMsgDialogAddr = PARAM(0);
    _log(INF, HLE, "sceUtilityMsgDialogInitStart(%08x)", PARAM(0));
    mem_readStruct(curMsgDialogAddr, &curMsgDialogParams, sizeof(SceUtilityMsgDialogParams));
    __sceUtilityWinInitStart();
    RETURN(0);
}

void sceUtilityMsgDialogShutdownStart()
{
    __sceUtilityWinShutdownStart();
    RETURN(0);
}

void sceUtilityMsgDialogGetStatus()
{
    u8 status = __sceUtilityWinGetStatus();
    _log(INF, HLE, "%d = sceUtilityMsgDialogGetStatus()", status);
    RETURN(status);
}

void sceUtilityMsgDialogUpdate()
{
    /* TODO: use the GUI if available */
    _log(INF, HLE, "sceUtilityMsgDialogUpdate(%d)", PARAM(0));
    if (__sceUtilityWinDoUpdate())
    {
        char buf[50];
        if (curMsgDialogParams.options & MSGDIALOG_OPTION_YESNO_BUTTONS)
        {
            _log(WRN, HLE, "Message: %s", curMsgDialogParams.message);
            _log(WRN, HLE, "Choose y(es), n(o) or c(ancel), or just return for default (%s):", (curMsgDialogParams.options & MSGDIALOG_OPTION_DEFAULT_NO) ? "no" : "yes");
            if (fgets(buf, 50, stdin))
            {
                if (buf[0] == 'y' || buf[0] == 'Y')
                    curMsgDialogParams.buttonPressed = MSGDIALOG_RESULT_YES;
                else if (buf[0] == 'n' || buf[0] == 'N')
                    curMsgDialogParams.buttonPressed = MSGDIALOG_RESULT_NO;
                else
                    curMsgDialogParams.buttonPressed = (curMsgDialogParams.options & MSGDIALOG_OPTION_DEFAULT_NO) ? MSGDIALOG_RESULT_NO : MSGDIALOG_RESULT_YES;
            }
        }
        else if (curMsgDialogParams.options & MSGDIALOG_OPTION_OK_BUTTON)
        {
            _log(WRN, HLE, "Message: %s", curMsgDialogParams.message);
            _log(WRN, HLE, "Choose o(k) or just return:");
            if (fgets(buf, 50, stdin))
                if (buf[0] == 'o' || buf[0] == 'O')
                    curMsgDialogParams.buttonPressed = MSGDIALOG_RESULT_YES;
        }
        else if (curMsgDialogParams.mode == MSGDIALOG_MODE_TEXT)
            _log(WRN, HLE, "Message: %s", curMsgDialogParams.message);
        else
            _log(WRN, HLE, "Message: Error %08x", curMsgDialogParams.errorValue);
        curMsgDialogParams.base.result = 0;
        mem_writeStruct(curMsgDialogAddr, &curMsgDialogParams, sizeof(curMsgDialogParams));
        __sceUtilityWinFinish();
    }

    RETURN(0);
}

/*****************
 * SYSTEM PARAMS *
 *****************/

void sceUtilityGetSystemParamInt()
{
    s32 defaultValues[16] = 
    {
        0, /* nickname */
        0, /* adhoc channel */
        0, /* wlan powersave */
        0, /* date notation */
        0, /* time notation */
        0, /* timezone offset in minutes */
        0, /* daylight savings */
        1, /* language (0=jap 1=eng) */
        0  /* unknown (region? X/O button?) */
    };
    _log(WRN, HLE, "sceUtilityGetSystemParamInt(%d, %08x) => %d", PARAM(0), PARAM(1), defaultValues[PARAM(0)]);
    mem_write32(PARAM(1), defaultValues[PARAM(0)]);
    RETURN(0);
}

/*******************************
 * UTILITY MODULES (UN)LOADING *
 *******************************/

void sceUtilityLoadModule()
{
    s32 module = PARAM(0);
    _log(INF, HLE, "sceUtilityLoadModule(%04x)", module);
    RETURN(0);
}

void sceUtilityLoadAvModule()
{
    _log(INF, HLE, "sceUtilityLoadAvModule(%d)", PARAM(0));
    RETURN(0);
}

void sceUtilityUnloadAvModule()
{
    _log(INF, HLE, "sceUtilityUnloadAvModule(%d)", PARAM(0));
    RETURN(0);
}

void sceUtilityLoadNetModule()
{
    _log(INF, HLE, "sceUtilityLoadNetModule(%d)", PARAM(0));
    RETURN(0);
}

void sceUtilityUnloadNetModule()
{
    _log(INF, HLE, "sceUtilityUnloadNetModule(%d)", PARAM(0));
    RETURN(0);
}

